#include "tcp_receiver.hh"

using namespace std;

void TCPReceiver::segment_received(const TCPSegment &seg) {
    size_t index{0};
    if (seg.header().syn) {
        _isn = seg.header().seqno;
        // if a segment with SYN flag carries payload
        // the stream index of payload starts with seqno + 1
        index += 1;
    }
    if (_isn == nullopt) {
        return;
    }
    bool eof = seg.header().fin;
    // a non-syn segment has the same seqno with syn segment
    // cause type size_t overflow
    if ((index == 0) && (unwrap(seg.header().seqno, _isn.value(), _reassembler.desired_index()) == 0)) {
        return;
    }
    // byte stream index = absolute seqno - 1
    index += unwrap(seg.header().seqno, _isn.value(), _reassembler.desired_index()) - 1;
    // no enough window size for this payload
    if (index > _reassembler.desired_index() && index - _reassembler.desired_index() >= window_size()) {
        return;
    }
    _reassembler.push_substring(seg.payload().copy(), index, eof);
}

optional<WrappingInt32> TCPReceiver::ackno() const {
    if (!_isn.has_value()) {
        return {};
    }
    // absolute seqno = byte stream index + 1
    uint64_t abs_seqno{_reassembler.desired_index() + 1};
    // if the receiver has already received the FIN segment,
    // seqno add an extra 1 that FIN occupies
    if (_reassembler.stream_out().input_ended()) {
        abs_seqno += 1;
    }
    WrappingInt32 desired_seqno{wrap(abs_seqno, _isn.value())};
    return desired_seqno;
}

size_t TCPReceiver::window_size() const { return _reassembler.stream_out().remaining_capacity(); }
